package org.marverse.util

import scala.collection.Set
import scala.collection.mutable.{HashMap, CloneableCollection}

/** The canonical factory methods for <a href="Map.html">mutable maps</a>.
 *  These currently return <a href="HashMap.html">HashMap's</a>.
 */
object MutableTypedMap {

  /** The empty map of this type; this is implemented as a proxy to a hashtable */  
  def empty[K[T] <: TypedKey[T], V]: MutableTypedMap[K, V] = new MutableTypedProxyMap[K, V](new HashMap())
  
  /** The canonical factory for this type
   */
  def apply[K[T] <: TypedKey[T], V](elems: (K[_ <: V], V)*) = empty[K, V] ++ elems

}

/** This trait represents mutable typed maps. Concrete map implementations
 *  just have to provide functionality for the abstract methods in
 *  <code>TypedMap</code> as well as for <code>update</code>,
 *  and <code>-=</code>.
 *
 *  @author  Matthias Zenger
 *  @author  Martin Odersky
 *  @author  Stefan Bohne
 */
@cloneable
trait MutableTypedMap[K[T] <: TypedKey[T], V] extends AnyRef with TypedMap[K, V] with CloneableCollection
{
  /** This method allows one to add a new mapping from <code>key</code>
   *  to <code>value</code> to the map. If the map already contains a
   *  mapping for <code>key</code>, it will be overridden by this
   *  function.
   *
   * @param key    The key to update
   * @param value  The new value
   */
  def update[V1 <: V](key: K[V1], value: V1)

  /** Add a key/value pair to this map. 
   *  @param    kv the key/value pair.
   */
  def +=[V1 <: V] (kv: (K[V1], V1)) { update(kv._1, kv._2) }

  /** Add two or more key/value pairs to this map. 
   *  @param    kv1 the first key/value pair.
   *  @param    kv2 the second key/value pair.
   *  @param    kvs the remaining key/value pairs.
   */
  def +=[V1 <: V, V2 <: V] (kv1: (K[V1], V1), kv2: (K[V2], V2), kvs: (K[_ <: V], V)*) {
    this += kv1; this += kv2; this ++= kvs
  }

  /** Add a sequence of key/value pairs to this map. 
   *  @param    kvs the iterable object containing all key/value pairs.
   */
  def ++= (kvs: Iterable[(K[_ <: V], V)]) { this ++= kvs.elements }

  /** Add a sequence of key/value pairs to this map. 
   *  @param    kvs the iterator containing all key/value pairs.
   */
  def ++= (kvs: Iterator[(K[_ <: V], V)]) { 
    for ((k, v) <- kvs) {
      this(k) = k.valueClass.cast(v)
    }
  }

  /** Add a key/value pair to this map. 
   *  @param    kv the key/value pair.
   *  @return   The map itself with the new binding added in place.
   */
  def +[V1 <: V] (kv: (K[V1], V1)): MutableTypedMap[K, V] = { this += kv; this }

  /** Add two or more key/value pairs to this map. 
   *  @param    kv1 the first key/value pair.
   *  @param    kv2 the second key/value pair.
   *  @param    kvs the remaining key/value pairs.
   *  @return   The map itself with the new bindings added in place.
   */
  def +[V1 <: V, V2 <: V] (kv1: (K[V1], V1), kv2: (K[V2], V2), kvs: (K[_ <: V], V)*): MutableTypedMap[K, V] = { 
    this.+=(kv1, kv2, kvs: _*); this 
  }

  /** Add a sequence of key/value pairs to this map. 
   *  @param    kvs the iterable object containing all key/value pairs.
   *  @return   The itself map with the new bindings added in place.
   */
  def ++ (kvs: Iterable[(K[_ <: V], V)]): MutableTypedMap[K, V] = { this ++= kvs; this }

  /** Add a sequence of key/value pairs to this map. 
   *  @param    kvs the iterator containing all key/value pairs.
   *  @return   The itself map with the new bindings added in place.
   */
  def ++ (kvs: Iterator[(K[_ <: V], V)]): MutableTypedMap[K, V] = { this ++= kvs; this }

  /** Remove a key from this map, noop if key is not present.
   *  @param    key the key to be removed
   */
  def -= (key: K[_])

  /** Remove two or more keys from this map
   *  @param    key1 the first key to be removed
   *  @param    key2 the second key to be removed
   *  @param    keys the remaining keys to be removed
   */
  def -= (key1: K[_], key2: K[_], keys: K[_]*) { this -= key1; this -= key2; this --= keys }

  /** Remove a sequence of keys from this map
   *  @param    keys the keys to be removed
   */
  def --= (keys: Iterable[K[_]]) { this --= keys.elements }

  /** Remove a sequence of keys from this map
   *  @param    keys the keys to be removed
   */
  def --= (keys: Iterator[K[_]]) { keys foreach -= }

  /** Remove a key from this map
   *  @param    key the key to be removed
   *  @return   The map itself with the binding for <code>key</code> removed if
   *            it existed.
   */
  def - (key: K[_]): MutableTypedMap[K, V] = { this -= key; this }

  /** Remove <code>key</code> from this map and return the element 
   *  that the key was previously mapped to (if any).
   */  
  def removeKey[V1](key: K[V1]): Option[V1] = {
    val ret = get(key)
    this -= key
    ret
  }

  /** Map <code>key</code> to <code>elem</code> in this map and return the element 
   *  that the key was previously mapped to (if any).
   */  
  def put[V1 <: V](key: K[V1], elem: V1): Option[V1] = {
    val ret = get(key)
    this(key) = elem
    ret
  }

  /** Remove two or more keys from this map
   *  @param    key1 the first key to be removed
   *  @param    key2 the second key to be removed
   *  @param    keys the remaining keys to be removed
   *  @return   The map itself with all bindings for the given keys removed.
   */
  def - (key1: K[_], key2: K[_ ], keys: K[_]*): MutableTypedMap[K, V] = { this.-=(key1, key2, keys: _*); this }

  /** Remove a sequence of keys from this map
   *  @param    keys the keys to be removed
   *  @return   The map itself with all bindings for <code>keys</code> removed.
   */
  def -- (keys: Iterable[K[_]]): MutableTypedMap[K, V] = { this --= keys; this }

  /** Remove a sequence of keys from this map
   *  @param    keys the keys to be removed
   *  @return   The map itself with all bindings for <code>keys</code> removed.
   */
  def -- (keys: Iterator[K[_]]): MutableTypedMap[K, V] = { this --= keys; this }

  /** Removes all mappings from the map. After this operation is
   *  completed, the map is empty.
   */
  def clear() { keys foreach -= }

  /** Check if this map maps <code>key</code> to a value.
    * Return that value if it exists, otherwise put <code>default</code>
    * as that key's value and return it.
    */
  def getOrElseUpdate[V1 <: V](key: K[V1], default: => V1): V1 =
    get(key) match {
      case Some(v) => v
      case None => val d = default; this(key) = d; d
    }

  /** This function transforms all the values of mappings contained
   *  in this map with function <code>f</code>.
   *
   * @param f  The transformation to apply
   */
  def transform(f: (K[_ <: V], V) => V) {
    elements foreach {
      case (key, value) => update(key, key.valueClass.cast(f(key, value)))
    }
  }

  /** This method retains only those mappings for which the predicate
   *  <code>p</code> returns <code>true</code>.
   *
   * @param p  The test predicate  
   * @deprecated cannot be type inferred because if retain in Iterable. 
   */
  def retain(p: (K[_ <: V], V) => Boolean) {
    toList foreach {
      case (key, value) => if (!p(key, value)) -=(key)
    }
  }

  /** Return a clone of this map.
   *
   *  @return a map with the same elements.
   */
  override def clone(): MutableTypedMap[K, V] = super.clone().asInstanceOf[MutableTypedMap[K, V]]

  /** Return a read-only projection of this map */
  def readOnly: TypedMap[K, V] = new TypedMap[K, V] {
    override def toString = "ro-" + MutableTypedMap.this.toString
    override def size = MutableTypedMap.this.size
    override def elements = MutableTypedMap.this.elements
    override def get[V1](key: K[V1]) = MutableTypedMap.this.get(key)
  }

}
